package com.dxj.service.impl;

import com.baomidou.mybatisplus.core.batch.MybatisBatch;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dxj.constant.SysConstants;
import com.dxj.controller.UserController;
import com.dxj.domain.Role;
import com.dxj.domain.User;
import com.dxj.domain.UserRole;
import com.dxj.exception.CustomException;
import com.dxj.exception.UserPasswordNotMatchException;
import com.dxj.mapper.PermissionMapper;
import com.dxj.mapper.RoleMapper;
import com.dxj.mapper.UserMapper;
import com.dxj.mapper.UserRoleMapper;
import com.dxj.request.LoginDTO;
import com.dxj.request.UserDTO;
import com.dxj.request.UserPageDTO;
import com.dxj.response.PageResultVo;
import com.dxj.response.R;
import com.dxj.response.UserInfoVo;
import com.dxj.response.UserRoleVo;
import com.dxj.security.domain.LoginUser;
import com.dxj.service.IUserService;
import com.dxj.sysEnum.SysHttpStatus;
import com.dxj.utils.JwtUtil;
import com.dxj.utils.RedisCache;
import com.dxj.utils.SecurityUtils;
import io.github.linpeilie.Converter;
import io.github.linpeilie.utils.CollectionUtils;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

/**
 * @author dxj
 * @Description 用户服务实现类
 * @date 2024/7/28 14:37
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    private static final Logger logger = LoggerFactory.getLogger(UserController.class);
    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    private RedisCache redisCache;

    @Resource
    private RoleMapper roleMapper;

    @Resource
    private PermissionMapper permissionMapper;


    @Resource
    private JwtUtil JwtUtil;

    @Resource
    private Converter converterMapper;

    @Resource
    private SqlSessionFactory sqlSessionFactory;

    @Resource
    private UserRoleMapper userRoleMapper;

    /**
     * 用户登录
     *
     * @param dto
     * @return
     */
    @Override
    public String login(LoginDTO dto) {
        removeCacheKey();
        Authentication authentication = null;
        try {
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(dto.getUserName(), dto.getPassWord());
            //执行UserServiceAuthImpl.loadUserByUsername
            authentication = authenticationManager.authenticate(authenticationToken);
        } catch (Exception e) {
            if (e instanceof BadCredentialsException) {
                throw new UserPasswordNotMatchException("用户名或密码错误！！！！");
            } else {
                throw new CustomException(e.getMessage());
            }
        }
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        //生成token
        return JwtUtil.createToken(loginUser);
    }

    /**
     * 退出登录
     */
    @Override
    public void removeToken() {
        //删除redis key
        removeCacheKey();
        //清除认证安全的上下文信息
        SecurityContextHolder.clearContext();
    }

    /**
     * 用户注册
     *
     * @param registerDTO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R register(UserDTO registerDTO) {
        //密码加密
        registerDTO.setPassWord(new BCryptPasswordEncoder().encode(registerDTO.getPassWord()));
        //用户是否已存在
        Long userCont = baseMapper.selectCount(new LambdaQueryWrapper<User>().eq(User::getUserName, registerDTO.getUserName()));
        if (userCont > 0) {
            return R.fail(SysHttpStatus.UNUSEREXIXTS);
        }
        User user = converterMapper.convert(registerDTO, User.class);
        baseMapper.insert(user);
        //维护用户角色
        List<Long> roleIds = registerDTO.getRoleIds();
        updateUserRole(roleIds, user.getId());
        return R.success();
    }

    /**
     * 用户信息获取通过id
     *
     * @param id
     * @return
     */
    @Override
    public R userInfo(Long id) {
        User user = baseMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getId, SecurityUtils.getUserId()));
        UserInfoVo userInfoVo = converterMapper.convert(user, UserInfoVo.class);
        //获取该用户角色信息
        userInfoVo.setRoles(roleMapper.getRoleById(userInfoVo.getId()));
        //获取该用户权限信息
        userInfoVo.setPermissions(permissionMapper.getUserPermissionById(user.getId()));
        return R.success("操作成功", userInfoVo);
    }

    /**
     * 用户信息更新
     *
     * @param userDTO
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R userUpdate(UserDTO userDTO) {
        //密码加密
        userDTO.setPassWord(new BCryptPasswordEncoder().encode(userDTO.getPassWord()));
        User user = converterMapper.convert(userDTO, User.class);
        user.setId(SecurityUtils.getUserId());
        //更新用户信息
        baseMapper.updateById(user);
        List<Long> roleIds = userDTO.getRoleIds();
        //删除旧的用户角色
        userRoleMapper.delete(new LambdaQueryWrapper<UserRole>().eq(UserRole::getUserId, user.getId()));
        //插入新的用户角色
        updateUserRole(roleIds, user.getId());
        return R.success();
    }

    /**
     * 删除用户
     *
     * @param id
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R userDelete(Long id) {
        baseMapper.deleteById(id);
        //删除userRole里面的user信息
        userRoleMapper.delete(new LambdaQueryWrapper<UserRole>().eq(UserRole::getUserId, id));
        return R.success();
    }

    /**
     * 分页查询
     *
     * @param userPageDTO
     * @return
     */
    @Override
    public R<PageResultVo<UserInfoVo>> userList(UserPageDTO userPageDTO) {
        //分页对象
        IPage<User> page = new Page<>(userPageDTO.getPageNum(), userPageDTO.getPageSize());
        //查询条件
        LambdaQueryWrapper<User> lambdaQuery = new LambdaQueryWrapper<>();
        lambdaQuery.select(User::getId, User::getEmail, User::getNickName, User::getUserName);
        lambdaQuery.likeLeft(StringUtils.hasText(userPageDTO.getUserName()), User::getUserName, userPageDTO.getUserName());
        lambdaQuery.likeLeft(StringUtils.hasText(userPageDTO.getNickName()), User::getNickName, userPageDTO.getNickName());

        // 异步查询数据库
        CompletableFuture<PageResultVo<UserInfoVo>> future = CompletableFuture.supplyAsync(() -> {
            try {
                // 调用 selectPage 分页方法
                IPage<User> userPage = baseMapper.selectPage(page, lambdaQuery);
                long total = userPage.getTotal();
                List<User> userList = userPage.getRecords();
                // 构建真正返回的数据
                List<UserInfoVo> userInfoVoList = userList.stream().map((item) -> {
                    UserInfoVo userInfoVo = converterMapper.convert(item, UserInfoVo.class);
                    return userInfoVo;
                }).collect(Collectors.toList());
                // 查询权限
                queryRole(userInfoVoList);
                PageResultVo<UserInfoVo> resultVo = new PageResultVo<>();
                resultVo.setTotal(total);
                resultVo.setRecords(userInfoVoList);
                return resultVo;
            } catch (Exception e) {
                // 处理异常，可以选择记录日志或者抛出运行时异常
                logger.error("查询用户列表时发生错误{}",e);
                throw new RuntimeException("查询用户列表失败", e);
            }
        });
        try {
            // 获取异步查询结果
            PageResultVo<UserInfoVo> resultVo = future.get();
            return R.success("操作成功", resultVo);
        } catch (InterruptedException | ExecutionException e) {
            // 处理异步执行时的异常
            logger.error("异步查询用户列表时发生错误{}",e);
            return R.fail("查询用户列表失败");
        }
    }

    /**
     * 查询用户权限 避免 n+1问题
     *
     * @param userInfoVoList
     */
    public void queryRole(List<UserInfoVo> userInfoVoList) {
        if (CollectionUtils.isEmpty(userInfoVoList)) {
            return;
        }
        List<Long> userIds = userInfoVoList.stream().map(UserInfoVo::getId).collect(Collectors.toList());
        List<UserRoleVo> userRoleVoList = roleMapper.getUserRoleByIds(userIds.toArray(Long[]::new));
        // 将用户角色列表转换为Map，键为用户ID，值为角色集合
        Map<Long, Set<String>> userRoleMap = userRoleVoList.stream()
                .collect(Collectors.groupingBy(UserRoleVo::getUserId,
                        Collectors.mapping(UserRoleVo::getRoleName, Collectors.toSet())));
        // 设置用户角色，从userRoleMap中获取与当前UserInfoVo对象用户ID对应的角色集合，找不到则设置空值
        userInfoVoList.forEach(userInfo -> {
            Set<String> roles = userRoleMap.getOrDefault(userInfo.getId(), Collections.emptySet());
            userInfo.setRoles(roles);
        });
    }

    /**
     * 删除token 每次登录删除之前cache里面的token信息,确保每次登录都是不同的key
     */
    public void removeCacheKey() {
        //获取之前存的token key
        String tokenKey = redisCache.get(SysConstants.TOKEN);
        //删除之前的key
        redisCache.delete(List.of(SysConstants.LOGIN_TOKEN_KEY + tokenKey, SysConstants.TOKEN));
    }

    /**
     * 维护用户角色
     *
     * @param roleIds
     * @param userId
     */
    public void updateUserRole(List<Long> roleIds, Long userId) {
        if (CollectionUtils.isNotEmpty(roleIds)) {
            List<UserRole> userRolesList = roleIds.stream()
                    .map(roleId -> {
                        UserRole userRole = new UserRole();
                        userRole.setUserId(userId);
                        userRole.setRoleId(roleId);
                        return userRole;
                    }).collect(Collectors.toList());
            //批量插入userRole
            if (userRolesList.size() > 0) {
                MybatisBatch<UserRole> mybatisBatch = new MybatisBatch<>(sqlSessionFactory, userRolesList);
                MybatisBatch.Method<UserRole> method = new MybatisBatch.Method<>(UserRoleMapper.class);
                mybatisBatch.execute(method.insert());
            }
        }
    }
}
